package com.ruoyi.web.entity.db;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.ruoyi.core.entity.BaseMap;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Data
public abstract class Creator {

    private BaseMap queryData = new BaseMap();

    private List<CreatorItem> where = new ArrayList<>();

    public Creator() {
    }

    public Creator(BaseMap queryData) {
        this.queryData = queryData;
    }

    public static <T> void toWrapper(QueryWrapper<T> qw, List<CreatorItem> items, BaseMap data) {
        for (Creator.CreatorItem item : items) {

            if (item.getChildren() != null && item.getChildren().size() > 0) {
                if (item.getConn().equals(ConnType.and)) {
                    qw.and(new Consumer<QueryWrapper<T>>() {
                        @Override
                        public void accept(QueryWrapper<T> tQueryWrapper) {
                            toWrapper(tQueryWrapper, item.getChildren(), data);
                        }
                    });
                } else if (item.getConn().equals(ConnType.or)) {
                    qw.or(new Consumer<QueryWrapper<T>>() {
                        @Override
                        public void accept(QueryWrapper<T> tQueryWrapper) {
                            toWrapper(tQueryWrapper, item.getChildren(), data);
                        }
                    });
                }
            } else {

                Object value = getValue(item, data);
                if (value == null || StringUtils.isEmpty(value.toString())) {
                    continue;
                }

                if (item.getConn().equals(ConnType.and)) {

                } else if (item.getConn().equals(ConnType.or)) {
                    qw.or();
                }

                switch (item.getOperator()) {
                    case eq:
                        qw.eq(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case ne:
                        qw.ne(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case lt:
                        qw.lt(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case le:
                        qw.le(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case gt:
                        qw.gt(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case ge:
                        qw.ge(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case like:
                        qw.like(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case notLike:
                        qw.notLike(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case likeLeft:
                        qw.likeLeft(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case likeRight:
                        qw.likeRight(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case notLikeLeft:
                        qw.notLikeLeft(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case notLikeRight:
                        qw.notLikeRight(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case between:
                        qw.between(resolveColumnName(item.getColumnName()), ((Object[]) getValue(item, data))[0], ((Object[]) getValue(item, data))[1]);
                        break;
                    case notBetween:
                        qw.notBetween(resolveColumnName(item.getColumnName()), ((Object[]) getValue(item, data))[0], ((Object[]) getValue(item, data))[1]);
                        break;
                    case in:
                        qw.in(resolveColumnName(item.getColumnName()), (Object[]) getValue(item, data));
                        break;
                    case notIn:
                        qw.notIn(resolveColumnName(item.getColumnName()), (Object[]) getValue(item, data));
                        break;
                    case isNull:
                        qw.isNull(resolveColumnName(item.getColumnName()));
                        break;
                    case isNotNull:
                        qw.isNotNull(resolveColumnName(item.getColumnName()));
                        break;
                }
            }

        }
    }

    public static <T> void toWrapper(UpdateWrapper<T> qw, List<CreatorItem> items, BaseMap data) {
        for (Creator.CreatorItem item : items) {

            if (item.getChildren() != null && item.getChildren().size() > 0) {
                if (item.getConn().equals(ConnType.and)) {
                    qw.and(new Consumer<UpdateWrapper<T>>() {
                        @Override
                        public void accept(UpdateWrapper<T> tQueryWrapper) {
                            toWrapper(tQueryWrapper, item.getChildren(), data);
                        }
                    });
                } else if (item.getConn().equals(ConnType.or)) {
                    qw.or(new Consumer<UpdateWrapper<T>>() {
                        @Override
                        public void accept(UpdateWrapper<T> tQueryWrapper) {
                            toWrapper(tQueryWrapper, item.getChildren(), data);
                        }
                    });
                }
            } else {
                if (item.getConn().equals(ConnType.and)) {

                } else if (item.getConn().equals(ConnType.or)) {
                    qw.or();
                }

                switch (item.getOperator()) {
                    case eq:
                        qw.eq(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case ne:
                        qw.ne(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case lt:
                        qw.lt(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case le:
                        qw.le(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case gt:
                        qw.gt(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case ge:
                        qw.ge(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case like:
                        qw.like(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case notLike:
                        qw.notLike(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case likeLeft:
                        qw.likeLeft(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case likeRight:
                        qw.likeRight(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case notLikeLeft:
                        qw.notLikeLeft(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case notLikeRight:
                        qw.notLikeRight(resolveColumnName(item.getColumnName()), getValue(item, data));
                        break;
                    case between:
                        qw.between(resolveColumnName(item.getColumnName()), ((Object[]) getValue(item, data))[0], ((Object[]) getValue(item, data))[1]);
                        break;
                    case notBetween:
                        qw.notBetween(resolveColumnName(item.getColumnName()), ((Object[]) getValue(item, data))[0], ((Object[]) getValue(item, data))[1]);
                        break;
                    case in:
                        qw.in(resolveColumnName(item.getColumnName()), (Object[]) getValue(item, data));
                        break;
                    case notIn:
                        qw.notIn(resolveColumnName(item.getColumnName()), (Object[]) getValue(item, data));
                        break;
                    case isNull:
                        qw.isNull(resolveColumnName(item.getColumnName()));
                        break;
                    case isNotNull:
                        qw.isNotNull(resolveColumnName(item.getColumnName()));
                        break;
                }
            }

        }
    }

    private static Pattern compile = Pattern.compile("[A-Z]");

    public static String resolveColumnName(String str) {
        Matcher matcher = compile.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    private static Object getValue(CreatorItem item, BaseMap data) {
        if (item.getValue() != null || data == null) {
            return item.getValue();
        }
        String key = !StringUtils.isEmpty(item.getKey()) ? item.getKey() : item.getColumnName();
        return data.getObject(key);
    }

    @Data
    public static class CreatorItem {

        private ConnType conn = ConnType.and;

        private String columnName;

        private OperatorType operator = OperatorType.eq;

        private String key;

        private Object value;

        private List<CreatorItem> children;

        public CreatorItem() {
        }

        public CreatorItem(ConnType conn) {
            this.conn = conn;
        }

        public CreatorItem(String columnName, Object value) {
            this.columnName = columnName;
            this.value = value;
        }

        public CreatorItem(String columnName, OperatorType operator, Object value) {
            this.columnName = columnName;
            this.operator = operator;
            this.value = value;
        }

        public CreatorItem(ConnType conn, String columnName, OperatorType operator, Object value) {
            this.conn = conn;
            this.columnName = columnName;
            this.operator = operator;
            this.value = value;
        }
    }
}
